﻿using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Data;
using System.Windows.Navigation;

namespace SMARTMobile.WinPhone
{
    /// <summary>
    /// Represent the based phone page. Every XAML page/view in this project should derive from it.
    /// </summary>
    /// <example>
    /// <core:CLPhonePage
    ///     x:Class="CL.Ehr.Client.WinPhone.Views.SamplePage"
    ///     xmlns:core="clr-namespace:CL.Ehr.Client.WinPhone.Core;assembly=CL.Ehr.Client.WinPhone.Core"
    ///     >
    ///     ...
    /// </core:CLPhonePage>
    /// 
    /// Remove the default PhoneApplicationPage as based class in the SamplePage also.
    /// </example>
    public class PhonePage : PhoneApplicationPage
    {
        private readonly SynchronizationContext _context;
        private ProgressIndicator _progressIndicator;
        bool _isNewPageInstance;
        private Uri _pageUri;

        protected PhonePage()
        {
            _context = SynchronizationContext.Current;
            Loaded += MobiPhonePageLoaded;
            Unloaded += MobiPhonePageUnloaded;
            
            _isNewPageInstance = true;
        }

        public LogService Log
        {
            get { return LogService.Current; }
        }

        protected virtual void OnPageLoaded(RoutedEventArgs e)
        {
        }

        protected virtual void OnPageUnloaded(RoutedEventArgs e)
        {
        }

        private void MobiPhonePageLoaded(object sender, RoutedEventArgs e)
        {
            // ProgressIndicator
            _progressIndicator = new ProgressIndicator { IsIndeterminate = true };
            Binding binding = new Binding("IsBusy") { Source = DataContext };
            BindingOperations.SetBinding(_progressIndicator, ProgressIndicator.IsVisibleProperty, binding);

            binding = new Binding("BusyText") { Source = DataContext };
            BindingOperations.SetBinding(_progressIndicator, ProgressIndicator.TextProperty, binding);
            SystemTray.SetProgressIndicator(this, _progressIndicator);
            
            // ApplicationBar
            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != viewModel)
            {
                if (null != viewModel.ApplicationBar)
                {
                    INotifyPropertyChanged pc = viewModel.ApplicationBar;
                    pc.PropertyChanged += ApplicationBarPropertyChanged;

                    this.ApplicationBar = CreateApplicationBar(viewModel.ApplicationBar);

                    // Buttons
                    viewModel.ApplicationBar.Buttons.OnCollectionDirty += AppBarButtonChanged;
                    viewModel.ApplicationBar.MenuItems.OnCollectionDirty += AppBarMenuItemChanged;
                    foreach (IApplicationBarIconButton button in viewModel.ApplicationBar.Buttons)
                    {
                        IApplicationBarIconButton barButton = CreateButton(button);
                        ApplicationBar.Buttons.Add(barButton);
                    }

                    // MenuItems
                    viewModel.ApplicationBar.MenuItems.OnCollectionDirty += AppBarButtonChanged;
                    foreach (IApplicationBarMenuItem menuItem in viewModel.ApplicationBar.MenuItems)
                    {
                        IApplicationBarMenuItem barButton = CreateMenuItem(menuItem);
                        ApplicationBar.MenuItems.Add(barButton);
                    }
                }
                viewModel.PageLoaded(_pageUri);
            }

            this.OnPageLoaded(e);
        }

        private void MobiPhonePageUnloaded(object sender, RoutedEventArgs e)
        {
            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null == viewModel) return;
            viewModel.PageUnloaded(_pageUri);
            
            if (null == ApplicationBar) return;

            INotifyPropertyChanged pc = viewModel.ApplicationBar;
            pc.PropertyChanged += ApplicationBarPropertyChanged;
            
            viewModel.ApplicationBar.Buttons.OnCollectionDirty -= AppBarButtonChanged;
            viewModel.ApplicationBar.Buttons.CollectionChanged -= AppBarButtonsCollectionChanged;
            viewModel.ApplicationBar.MenuItems.OnCollectionDirty -= AppBarMenuItemChanged;
            viewModel.ApplicationBar.MenuItems.CollectionChanged -= AppBarMenuItemsCollectionChanged;


            foreach (ApplicationBarIconButton button in ApplicationBar.Buttons)
            {
                button.Click -= ApplicationBarIconButtonClick;
            }
            foreach (ApplicationBarMenuItem menu in ApplicationBar.MenuItems)
            {
                menu.Click -= ApplicationBarMenuItemClick;
            }
            ApplicationBar = null;

            this.OnPageUnloaded(e);
        }
        
        protected override void OnBackKeyPress(CancelEventArgs e)
        {
            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != viewModel)
            {
                viewModel.OnBackKeyPress(e);
            }
            base.OnBackKeyPress(e);
        }

        private void ApplicationBarPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(e.PropertyName)) return;

            PhoneApplicationBar applicationBar = sender as PhoneApplicationBar;
            if (null == applicationBar) return;

            switch (e.PropertyName)
            {
                case "IsVisible":
                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                                                                  {
                                                                      if (null != ApplicationBar)
                                                                      {
                                                                          ApplicationBar.IsVisible =
                                                                              applicationBar.IsVisible;
                                                                      }
                                                                  });
                    break;
                //case "":
                //    break;
                //case "":
                //    break;
            }
        }

        private void AppBarButtonChanged(object sender, PropertyChangedEventArgs e)
        {
            IApplicationBarIconButton viewModelButton = sender as IApplicationBarIconButton;
            Deployment.Current.Dispatcher.BeginInvoke(() =>
                                                          {
                                                              var button = (from IApplicationBarIconButton b in ApplicationBar.Buttons
                                                                            where b.Text == viewModelButton.Text
                                                                            select b).SingleOrDefault();
                                                              if (null != button)
                                                              {
                                                                  // get value off ViewModel button....
                                                                  Type type = viewModelButton.GetType();
                                                                  PropertyInfo info = type.GetProperty(e.PropertyName);
                                                                  object value = info.GetValue(viewModelButton, null);

                                                                  // set value to System button
                                                                  type = button.GetType();
                                                                  info = type.GetProperty(e.PropertyName);
                                                                  info.SetValue(button, value, null);
                                                              }
                                                          });
        }

        private void AppBarMenuItemChanged(object sender, PropertyChangedEventArgs e)
        {
            IApplicationBarMenuItem viewModelMenuItem = sender as IApplicationBarMenuItem;
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                var menuItem = (from IApplicationBarMenuItem mi in ApplicationBar.Buttons
                                where mi.Text == viewModelMenuItem.Text
                                select mi).SingleOrDefault();
                if (null != menuItem)
                {
                    // get value off ViewModel button....
                    Type type = viewModelMenuItem.GetType();
                    PropertyInfo info = type.GetProperty(e.PropertyName);
                    object value = info.GetValue(viewModelMenuItem, null);

                    // set value to System button
                    type = menuItem.GetType();
                    info = type.GetProperty(e.PropertyName);
                    info.SetValue(menuItem, value, null);
                }
            });
        }

        private void AppBarButtonsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                if (null != e.OldItems)
                {
                    foreach (IApplicationBarIconButton item in e.OldItems)
                    {
                        var itemToRemove =
                            (from IApplicationBarIconButton b in ApplicationBar.Buttons
                             where b.Text == item.Text
                             select b).SingleOrDefault();
                        ApplicationBar.Buttons.Remove(itemToRemove);
                    }
                }
                if (null == e.NewItems)
                    return;

                foreach (IApplicationBarIconButton item in e.NewItems)
                {
                    ApplicationBar.Buttons.Add(
                        CreateButton(item as IApplicationBarIconButton));
                }
            });
        }

        private void AppBarMenuItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                if (e.Action == NotifyCollectionChangedAction.Reset)
                {
                    ApplicationBar.MenuItems.Clear();
                }

                if (null != e.OldItems)
                {
                    foreach (IApplicationBarMenuItem item in e.OldItems)
                    {
                        var itemToRemove =
                            (from IApplicationBarMenuItem b in ApplicationBar.MenuItems
                             where b.Text == item.Text
                             select b).SingleOrDefault();
                        ApplicationBar.MenuItems.Remove(itemToRemove);
                    }
                }

                if (null == e.NewItems)
                    return;

                foreach (IApplicationBarMenuItem item in e.NewItems)
                {
                    ApplicationBar.MenuItems.Add(
                        CreateMenuItem(item as IApplicationBarMenuItem));
                }
            });
        }

        public void OnStateChanged(object sender, ApplicationBarStateChangedEventArgs e)
        {
            IApplicationBar bar = sender as IApplicationBar;
            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != bar && null != viewModel)
            {
                viewModel.ApplicationBarStateChanged(e.IsMenuVisible);
            }
        }

        protected virtual void ApplicationBarIconButtonClick(object sender, EventArgs e)
        {
            Focus(); // force databinding

            // this gives enough of a delay to make it happen
            _context.Post(OnApplicationBarIconButtonClick, sender);
        }

        private void OnApplicationBarIconButtonClick(object state)
        {
            IApplicationBarIconButton button = state as IApplicationBarIconButton;
            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != button && null != viewModel)
            {
                viewModel.ApplicationBarButtonClick(button);
            }
        }

        protected virtual void ApplicationBarMenuItemClick(object sender, System.EventArgs e)
        {
            Focus(); // force databinding

            // this gives enough of a delay to make it happen
            _context.Post(OnApplicationBarMenuItemClick, sender);
        }

        private void OnApplicationBarMenuItemClick(object state)
        {
            IApplicationBarMenuItem menuItem = state as IApplicationBarMenuItem;
            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != menuItem && null != viewModel)
            {
                viewModel.ApplicationBarMenuClick(menuItem);
            }
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            while (NavigationService.RemoveBackEntry() != null) ;

            Debug.WriteLine("MobiPhonePage::OnNavigatedTo mode={0}, type={1}", e.NavigationMode, GetType().FullName);

            _pageUri = e.Uri;

            if (NavigationContext.QueryString.ContainsKey("pop"))
            {
                int count = int.Parse(NavigationContext.QueryString["pop"]);
                for (int x = 0; x < count; x++)
                {
                    if (NavigationService.CanGoBack)
                    {
                        NavigationService.RemoveBackEntry();
                    }
                }
            }

            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != viewModel)
            {
                if (_isNewPageInstance && State.Count > 0)
                {
                    viewModel.RestoreState(State);
                    Debug.WriteLine("State.Count == {0}", State.Count);
                }
                else if (_isNewPageInstance && e.NavigationMode == NavigationMode.New)
                {
                    viewModel.Initialize(e.Uri);
                }
                viewModel.NavigatedTo(e.NavigationMode, e.Uri, NavigationContext.QueryString);
            }
            base.OnNavigatedTo(e);
        }
        
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            Debug.WriteLine("MobiPhonePage::OnNavigatedFrom mode={0}, type={1}", e.NavigationMode, GetType().FullName);

            IWinPhoneViewModel viewModel = DataContext as IWinPhoneViewModel;
            if (null != viewModel)
            {
                viewModel.NavigatedFrom(e.NavigationMode, e.Uri, NavigationContext.QueryString);
            }
            if (NavigationMode.Back != e.NavigationMode)
            {
                if (viewModel != null)
                {
                    viewModel.SaveState(State);
                }
            }
            base.OnNavigatedFrom(e);
            _isNewPageInstance = false;
        }

        private IApplicationBar CreateApplicationBar(PhoneApplicationBar applicationBar)
        {
            ApplicationBar bar = new ApplicationBar
            {
                IsVisible = applicationBar.IsVisible,
                IsMenuEnabled = applicationBar.IsMenuEnabled,
                Opacity = applicationBar.Opacity,
                ForegroundColor = applicationBar.ForegroundColor,
                BackgroundColor = applicationBar.BackgroundColor
            };

            applicationBar.Buttons.CollectionChanged += AppBarButtonsCollectionChanged;
            applicationBar.MenuItems.CollectionChanged += AppBarMenuItemsCollectionChanged;
            bar.StateChanged += OnStateChanged;
            return bar;
        }

        private IApplicationBarIconButton CreateButton(IApplicationBarIconButton button)
        {
            ApplicationBarIconButton iconButton = new ApplicationBarIconButton
                                                      {
                                                          IconUri = button.IconUri,
                                                          IsEnabled = button.IsEnabled,
                                                          Text = button.Text
                                                      };

            iconButton.Click += ApplicationBarIconButtonClick;
            return iconButton;
        }

        private IApplicationBarMenuItem CreateMenuItem(IApplicationBarMenuItem menuItem)
        {
            ApplicationBarMenuItem item = new ApplicationBarMenuItem
                                              {
                                                  IsEnabled = menuItem.IsEnabled,
                                                  Text = menuItem.Text
                                              };

            item.Click += ApplicationBarMenuItemClick;
            return item;
        }
    }
}
